﻿#region

using System;
using System.Collections;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Security;
using HouseHoldBudget.Core.Authentication;
using HouseHoldBudget.Core.Entities.User;
using HouseHoldBudget.Framework.Session;

#endregion

namespace HouseHoldBudget.Framework.Authentication
{
    public class BudgetFormsAuthenticationService
    {
        #region Readonly & Static Fields

        private static bool _Initialized;
        private static string _LoginUrl;
        private static readonly object _lockObject = new object();

        #endregion

        #region Class Properties

        /// <summary>
        ///   Gets the login URL.
        /// </summary>
        public static string LoginUrl
        {
            get
            {
                Initialize();
                if ((_LoginUrl.Length != 0) && ((_LoginUrl[0] == '/') || (_LoginUrl.IndexOf("//", StringComparison.Ordinal) >= 0)))
                {
                    return _LoginUrl;
                }
                return ("/" + _LoginUrl);
            }
        }

        #endregion

        #region Class Methods

        /// <summary>
        ///   Retourne une instance UserIdentity contenu dans une chaine encryptée
        /// </summary>
        /// <param name="encryptedInput"> chaine cryptée </param>
        /// <returns> UserIdentity </returns>
        public static BudgetUser Decrypt(string encryptedInput)
        {
            BudgetUser identity = null;
            try
            {
                ArrayList userProperties = new ArrayList();
                Split("|$|", encryptedInput, userProperties);

                if (userProperties != null && userProperties.Count > 0)
                {
                    identity = new BudgetUser();
                    Type t_identity = identity.GetType();
                    PropertyInfo[] propertyInfo = t_identity.GetProperties();
                    for (int i = 0; i < propertyInfo.Length; i++)
                    {
                        PropertyInfo property = propertyInfo[i];
                        if (property.CanWrite)
                        {
                            string propertyValue = userProperties[i].ToString();

                            if (property.PropertyType.IsEnum)
                            {
                                object objValue = Enum.Parse(property.PropertyType, propertyValue); // Convert.ChangeType(propertyValue, property.PropertyType);
                                property.SetValue(identity, objValue, null);
                            }
                            else
                            {
                                object objValue = Convert.ChangeType(propertyValue, property.PropertyType);
                                property.SetValue(identity, objValue, null);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                string str = e.Message;
                throw;
            }
            return identity;
        }

        /// <summary>
        ///   Crypte une instance UserIdentity et retourne la chaine cryptée
        /// </summary>
        /// <param name="identity"> UserIdentity </param>
        /// <returns> chaine </returns>
        public static string Encrypt(BudgetUser identity)
        {
            string encryptedString = String.Empty;
            try
            {
                StringBuilder en_str = new StringBuilder();
                Type t_Identity = identity.GetType();
                PropertyInfo[] propertyInfo = t_Identity.GetProperties();
                foreach (PropertyInfo property in propertyInfo)
                {
                    en_str.Append(property.GetValue(identity, null));
                    en_str.Append("|$|");
                }
                encryptedString = en_str.ToString();
            }
            catch (Exception e)
            {
                string str = e.Message;
                throw;
            }
            return encryptedString;
        }

        /// <summary>
        ///   Extrait le cookie d'authentification et le decrypt
        /// </summary>
        /// <returns> </returns>
        public static BudgetUser ExtractUserFromCookie()
        {
            BudgetUser budgetUserIdentity = null;
            HttpCookie cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];

            if (cookie != null)
            {
                var authTicket = FormsAuthentication.Decrypt(cookie.Value);
                if (!string.IsNullOrEmpty(authTicket.UserData))
                {
                    budgetUserIdentity = Decrypt(authTicket.UserData);
                }
            }
            return budgetUserIdentity;
        }

        /// <summary>
        ///   Initialise la classe static
        /// </summary>
        public static void Initialize()
        {
            if (!_Initialized)
            {
                lock (_lockObject)
                {
                    if (_LoginUrl == null)
                    {
                        _LoginUrl = "Account/LogOn";
                    }
                    _Initialized = true;
                }
            }
        }

        /// <summary>
        ///   Signs the in.
        /// </summary>
        /// <param name="budgetUser"> The user. </param>
        public static void SetAuthentication(BudgetIdentity budgetUser)
        {
            // Create and encrypt the ticket 
            //budgetUser.IsAuthenticated = true;
            var ticket = new FormsAuthenticationTicket(
                1,
                budgetUser.CurrentUser.UserFirstName,
                DateTime.Now,
                DateTime.Now.AddMinutes(30),
                false,
                string.Empty
                );
            var encTicket = FormsAuthentication.Encrypt(ticket);
            HttpCookie authCookie =
                new HttpCookie(FormsAuthentication.FormsCookieName,
                               encTicket);

            //Append in session
            if (HttpContext.Current.Session != null)
                SessionPersister.CurrentBudgetUserIdentity = budgetUser;

            HttpContext.Current.User = new UserPrincipal(budgetUser);

            // Append the cookie to the response 
            HttpContext.Current.Response.Cookies.Add(authCookie);
        }

        /// <summary>
        ///   Signs the out.
        /// </summary>
        public static void SignOut()
        {
            SessionPersister.CurrentBudgetUserIdentity = null;
            FormsAuthentication.SignOut();
        }


        /// <summary>
        ///   Used to split a string into an string array based on a separator
        /// </summary>
        /// <param name="seperator"> </param>
        /// <param name="str"> </param>
        /// <param name="strArray"> </param>
        private static void Split(string seperator, string str, ArrayList strArray)
        {
            try
            {
                int start = 0, end = 0;
                end = str.IndexOf(seperator);
                if (end <= -1)
                {
                    end = str.Length;
                    strArray.Add(str.Substring(start, end - start));
                    return;
                }
                strArray.Add(str.Substring(start, end - start));

                start = end + seperator.Length;
                end = str.Length;

                Split(seperator, str.Substring(start, end - start), strArray);
            }
            catch (Exception ex)
            {
                throw (ex);
            }
        }

        #endregion
    }
}